package com.zzh.bridge_processor;


import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeSpec;
import com.zzh.bridge_annotation.TagImpl;
import com.zzh.bridge_annotation.Moudle;
import com.zzh.bridge_annotation.RouteTag;
import com.zzh.bridge_annotation.Route;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;


@AutoService(Processor.class)
public class BridgeProcessor extends AbstractProcessor {


    private Messager mMessager;
    private Elements mElements;
    private Filer mFiler;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        mFiler = processingEnv.getFiler();
        mElements = processingEnv.getElementUtils();
        mMessager = processingEnv.getMessager();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        HashSet<String> supportTypes = new LinkedHashSet<>();
        supportTypes.add(RouteTag.class.getCanonicalName());
        return supportTypes;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        page(roundEnv);
        return true;
    }

    /**
     * 处理page界面处理
     *
     * @param roundEnv
     */
    private void page(RoundEnvironment roundEnv) {
        Set<? extends Element> pageElements = roundEnv.getElementsAnnotatedWith(RouteTag.class);
        if (pageElements == null || pageElements.size() <= 0) {
            return;
        }
        Set<? extends Element> moudleElements = roundEnv.getElementsAnnotatedWith(Moudle.class);
        if (moudleElements == null || moudleElements.size() <= 0) {
            mMessager.printMessage(Diagnostic.Kind.ERROR, "请给对应的模块添加 @Moudel 类");
        }
        if (moudleElements.size() > 1) {
            mMessager.printMessage(Diagnostic.Kind.ERROR, "暂不支持一个模块 多个@Moudel 类操作");
        }
        // 获取moudle配置的路由type
        Element moudleElement = moudleElements.iterator().next();
        Moudle moudle = moudleElement.getAnnotation(Moudle.class);
        List<? extends Element> moudleElementMembers = mElements.getAllMembers((TypeElement) moudleElement);
        List<String> routeStrs = new ArrayList<>();
        for (Element moudleElementMember : moudleElementMembers) {
            Route route = moudleElementMember.getAnnotation(Route.class);
            if (route != null) {
                VariableElement variableElement = (VariableElement) moudleElementMember;
                Object value = variableElement.getConstantValue();
                if (value instanceof String) {
                    routeStrs.add((String) value);
                } else {
                    mMessager.printMessage(Diagnostic.Kind.ERROR, "@Route 只能使用String类型");
                }
            }
        }
        // 编写moudel执行类
        writeMoudelClass(moudle.moudelName(), routeStrs, pageElements);
    }

    private void writeMoudelClass(String moudelName, List<String> routeStrs, Set<? extends Element> pageElements) {
        //得到一个不存在的类
        ClassName intent = ClassName.get("com.zzh.bridge", "Bridge");

        // 获取名字的方法
        MethodSpec getName = MethodSpec.methodBuilder("getName")
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .returns(String.class)
                .addStatement("return $S", moudelName)
                .build();
        TypeSpec moudel = TypeSpec.classBuilder("Moudel_" + moudelName)
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addAnnotation(TagImpl.class)
                .superclass(intent)
                .addMethod(getName)
                .addMethod(get(routeStrs, pageElements))
                .addMethod(getStartMethodSpec())
                .build();
        try {
            JavaFile javaFile = JavaFile.builder("com.zzh.page", moudel)
                    .addFileComment(" moudel 操作类")
                    .build();
            javaFile.writeTo(mFiler);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private MethodSpec get(List<String> routeStrs, Set<? extends Element> pageElements) {
        MethodSpec.Builder builder = MethodSpec.methodBuilder("get")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Override.class)
                .returns(Class.class)
                .addParameter(ParameterSpec.builder(String.class, "pageType")/*.addAnnotation(NotNull)*/.build());
        StringBuilder stringBuilder = new StringBuilder().append("Class cls = null;\n" +
                "switch (pageType) {\n");
        for (Element pageElement : pageElements) {
            // 获取类名
            String className = pageElement.getSimpleName().toString();
            // 获取注解对象
            RouteTag page = pageElement.getAnnotation(RouteTag.class);
            String name = page.value();
            if (!routeStrs.contains(name)) {
                break;
            }
            // 获取包的节点
            PackageElement packageElement = mElements.getPackageOf(pageElement);
            String packageName = packageElement.getQualifiedName().toString();
            stringBuilder.append("  case ").append("\"").append(name).append("\":cls = ").append(packageName).append('.').append(className).append(".class;").append("break;\n");
        }
        stringBuilder.append("  default:" +
                "break;\n" +
                "}\n" +
                "if (cls == null) {\n" +
                "   android.util.Log.i(TAG, \"没有找到指定类\" + pageType);return null;\n" +
                "}\n" +
                "return cls");
        builder.addStatement(stringBuilder.toString());
        return builder.build();
    }


    private MethodSpec getStartMethodSpec() {
        return MethodSpec.methodBuilder("start")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Override.class)
                .addParameter(ParameterSpec.builder(String.class, "pageType").build())
                .addParameter(ParameterSpec.builder(ClassName.get("android.content", "Context"), "context").build())
                .addParameter(ClassName.get("android.os", "Bundle"), "bundle")
                .addParameter(int.class, "requestCode")
                .addStatement("Class cls = get(pageType);\nif (cls == null) {\n   android.util.Log.i(TAG, \"没有找到指定界面\" + pageType);return;\n}\nstartActivity(cls, context,bundle,requestCode)")
                .build();
    }


}